#include "singleton_container.h"

#include <mutex>

#include "log.h"

const sptr<SingletonContainer> &SingletonContainer::GetInstance()
{
    if (instance == nullptr) {
        static std::mutex mutex;
        std::lock_guard<std::mutex> lock(mutex);
        if (instance == nullptr) {
            instance = new SingletonContainer();
        }
    }
    return instance;
}

SingletonContainer::~SingletonContainer()
{
    while (singletonMap.empty() == false) {
        auto it = singletonMap.begin();
        while (it != singletonMap.end()) {
            if (it->second.refCount > 0) {
                it++;
                continue;
            }

            if (dependencySetMap.find(it->first) != dependencySetMap.end()) {
                for (auto mid : dependencySetMap[it->first]) {
                    singletonMap[mid].refCount--;
                }
                dependencySetMap.erase(it->first);
            }

            for (const auto &[k, v] : stringMap) {
                if (v == it->first) {
                    LOG("remove %s", k.c_str());
                    break;
                }
            }
            singletonMap.erase(it++);
        }
    }
}

void SingletonContainer::AddSingleton(const std::string &name, const std::any &instance)
{
    if (stringMap.find(name) == stringMap.end()) {
        static int32_t nextId = 0;
        singletonMap[nextId].value = instance;
        singletonMap[nextId].refCount = 0;
        LOG("add %s", name.c_str());
        stringMap[name] = nextId++;
    } else {
        LOG("add failed: %s", name.c_str());
    }
}

void SingletonContainer::SetSingleton(const std::string &name, const std::any &instance)
{
    if (stringMap.find(name) == stringMap.end()) {
        AddSingleton(name, instance);
    } else {
        LOG("set %s", name.c_str());
        singletonMap[stringMap[name]].value = instance;
    }
}

const std::any& SingletonContainer::GetSingleton(const std::string &name)
{
    if (stringMap.find(name) == stringMap.end()) {
        LOG("cannot get %s", name.c_str());
        static std::any voidAny;
        return voidAny;
    }
    return singletonMap[stringMap[name]].value;
}

const std::any& SingletonContainer::DependOn(const std::string &instance, const std::string &name)
{
    auto &instanceDependencySet = dependencySetMap[stringMap[instance]];
    if (instanceDependencySet.find(stringMap[name]) == instanceDependencySet.end()) {
        LOG("%s dependon %s", instance.c_str(), name.c_str());
        instanceDependencySet.insert(stringMap[name]);
        singletonMap[stringMap[name]].refCount++;
    }
    return GetSingleton(name);
}
